package decaf.translate;

import java.util.Stack;

import decaf.tree.Tree;
import decaf.backend.OffsetCounter;
import decaf.machdesc.Intrinsic;
import decaf.symbol.Variable;
import decaf.tac.Label;
import decaf.tac.Temp;
import decaf.type.BaseType;

public class TransPass2 extends Tree.Visitor 
{

	private Translater tr;

	private Temp currentThis;

	private Stack<Label> loopExits;

	public TransPass2(Translater tr) 
	{
		this.tr = tr;
		loopExits = new Stack<Label>();
	}

	@Override
	public void visitClassDef(Tree.ClassDef classDef) 
	{
		for (Tree f : classDef.fields) 
		{
			f.accept(this);
		}
	}

	@Override
	public void visitMethodDef(Tree.MethodDef funcDefn)
	{
		if (!funcDefn.statik) 
		{
			currentThis = ((Variable) funcDefn.symbol.getAssociatedScope().lookup("this")).getTemp();
			//引用该非静态函数实例的临时变量
		}
		tr.beginFunc(funcDefn.symbol);	//处理symbol
		funcDefn.body.accept(this);
		tr.endFunc();
		currentThis = null;
	}

	@Override
	public void visitTopLevel(Tree.TopLevel program) 
	{
		for (Tree.ClassDef cd : program.classes) 
		{
			cd.accept(this);
		}
	}

	@Override
	public void visitVarDef(Tree.VarDef varDef) 
	{
		if (varDef.symbol.isLocalVar())
		{
			Temp t = Temp.createTempI4();//创建Temp
			t.sym = varDef.symbol;	//为Temp赋值
			varDef.symbol.setTemp(t);	//为VarDef关联Temp对象
		}
	}

	@Override
	public void visitBinary(Tree.Binary expr) //Binary——Expr.Temp--by me
	{
		expr.left.accept(this);
		expr.right.accept(this);
    	switch (expr.tag) 
    	{
			case Tree.PLUS:
				expr.val = tr.genAdd(expr.left.val,expr.right.val);
				break;
			case Tree.MINUS:
				expr.val = tr.genSub(expr.left.val,expr.right.val);
				break;
			case Tree.MUL:
				expr.val = tr.genMul(expr.left.val,expr.right.val);
				break;
			case Tree.DIV:
				expr.val = tr.genDiv(expr.left.val,expr.right.val);
				break;
			case Tree.MOD:
				expr.val = tr.genMod(expr.left.val,expr.right.val);
				break;
			case Tree.AND:
				expr.val = tr.genLAnd(expr.left.val,expr.right.val);
				break;
			case Tree.OR:
				expr.val = tr.genLOr(expr.left.val,expr.right.val);
				break;
			//helped
			case Tree.EQ:
				if (expr.left.type.equal(BaseType.STRING) || expr.right.type.equal(BaseType.STRING)) 
				{
					tr.genParm(expr.left.val);
					tr.genParm(expr.right.val);
					expr.val = tr.genDirectCall(Intrinsic.STRING_EQUAL.label, BaseType.BOOL);			
				}
				else
					expr.val = tr.genEqu(expr.left.val,expr.right.val);
				break;
			case Tree.NE:
				if (expr.left.type.equal(BaseType.STRING) || expr.right.type.equal(BaseType.STRING)) 
				{
					tr.genParm(expr.left.val);
					tr.genParm(expr.right.val);
					expr.val = tr.genLNot(tr.genDirectCall( Intrinsic.STRING_EQUAL.label, BaseType.BOOL));
				}
				else
					expr.val = tr.genNeq(expr.left.val,expr.right.val);
				break;
			case Tree.LT:
				expr.val = tr.genLes(expr.left.val,expr.right.val);
					break;
			case Tree.LE:
				expr.val = tr.genLeq(expr.left.val,expr.right.val);
				break;
			case Tree.GT:
				expr.val = tr.genGtr(expr.left.val,expr.right.val);
				break;
			case Tree.GE:
				expr.val = tr.genGeq(expr.left.val,expr.right.val);
				break;
    	}	
	}

	@Override
	public void visitAssign(Tree.Assign assign) //Assign——Tree--by me
	{
		assign.left.accept(this);
    	assign.expr.accept(this);
    	//helped!!!!!!!!
    	switch (assign.left.lvKind)
    	{
			case LOCAL_VAR:
			case PARAM_VAR:
				tr.genAssign(assign.left.val,assign.expr.val);
				break;
			case MEMBER_VAR:
			{				
				Tree.Ident ident =  (Tree.Ident)(assign.left);
				if (ident.owner != null) 
					tr.genStore(assign.expr.val, ident.owner.val, ident.symbol.getOffset());
				else 
					tr.genStore(assign.expr.val, currentThis, ident.symbol.getOffset());			
				break;
			}
			case ARRAY_ELEMENT:
			{
				Tree.Indexed indexed =  (Tree.Indexed)assign.left;
				Temp esz = tr.genLoadImm4(OffsetCounter.WORD_SIZE);
				Temp t = tr.genMul(indexed.index.val, esz);
				Temp base = tr.genAdd(indexed.array.val, t);
				tr.genStore(assign.expr.val,base,0);//已经分配了空间
				break;
			}
		}
	}

	@Override
	public void visitLiteral(Tree.Literal literal)//Literal——Expr.Temp
	{
		switch (literal.typeTag)
		{
			case Tree.INT:
				literal.val = tr.genLoadImm4(((Integer)literal.value).intValue());
				break;
			case Tree.BOOL:
				literal.val = tr.genLoadImm4((Boolean)(literal.value) ? 1 : 0);
				break;
			default:
				literal.val = tr.genLoadStrConst((String)literal.value);
		}
	}

	@Override
	public void visitExec(Tree.Exec exec) //Exec——Tree
	{
		exec.expr.accept(this);
	}

	@Override
	public void visitUnary(Tree.Unary expr) //Unary——Expr.Temp
	{
		expr.expr.accept(this);
		switch (expr.tag)
		{
			case Tree.NEG:
				expr.val = tr.genNeg(expr.expr.val);
				break;
			default:
				expr.val = tr.genLNot(expr.expr.val);
		}
	}

	@Override
	public void visitNull(Tree.Null nullExpr) //Null——Expr.Temp
	{
		nullExpr.val = tr.genLoadImm4(0);
	}

	@Override
	public void visitBlock(Tree.Block block) 
	{
		for (Tree s : block.block) 
		{
			s.accept(this);
		}
	}

	@Override
	public void visitThisExpr(Tree.ThisExpr thisExpr)//ThisExpr——Expr.Temp 
	{
		thisExpr.val = currentThis;
	}

	@Override
	public void visitReadIntExpr(Tree.ReadIntExpr readIntExpr) //ReadIntExpr——Expr.Temp
	{
		readIntExpr.val = tr.genIntrinsicCall(Intrinsic.READ_INT);
	}

	@Override
	public void visitReadLineExpr(Tree.ReadLineExpr readStringExpr) //ReadLineExpr——Expr.Temp
	{
		readStringExpr.val = tr.genIntrinsicCall(Intrinsic.READ_LINE);
	}

	@Override
	public void visitReturn(Tree.Return returnStmt) //Return——Tree
	{
		if (returnStmt.expr != null) 
		{
			returnStmt.expr.accept(this);
			tr.genReturn(returnStmt.expr.val);
		} 
		else 
		{
			tr.genReturn(null);
		}

	}

	@Override
	public void visitPrint(Tree.Print printStmt) //Print——Tree--by me
	{
    	for (Tree.Expr e : printStmt.exprs) 
    	{
			e.accept(this);
			tr.genParm(e.val);			
			if ( e.type == BaseType.INT )
				e.val = tr.genIntrinsicCall(Intrinsic.PRINT_INT);			
			else if( e.type == BaseType.BOOL )
				e.val = tr.genIntrinsicCall(Intrinsic.PRINT_BOOL);
			else if( e.type == BaseType.STRING )
				e.val = tr.genIntrinsicCall(Intrinsic.PRINT_STRING);
    	}
	}

	@Override
	public void visitIndexed(Tree.Indexed indexed) //Indexed——LValue——Expr.Temp
	{
		indexed.array.accept(this);
		indexed.index.accept(this);
		tr.genCheckArrayIndex(indexed.array.val, indexed.index.val);//Error Check
		
		Temp esz = tr.genLoadImm4(OffsetCounter.WORD_SIZE);
		Temp t = tr.genMul(indexed.index.val, esz);
		Temp base = tr.genAdd(indexed.array.val, t);
		indexed.val = tr.genLoad(base, 0);
	}

	@Override
	public void visitIdent(Tree.Ident ident) //Ident——LValue——Expr.Temp
	{
		if(ident.lvKind == Tree.LValue.Kind.MEMBER_VAR)
		{
			ident.owner.accept(this);
		}
		
		switch (ident.lvKind) 
		{
			case MEMBER_VAR:
				ident.val = tr.genLoad(ident.owner.val, ident.symbol.getOffset());
				break;
			default:
				ident.val = ident.symbol.getTemp();
				break;
		}
	}
	
	@Override
	public void visitBreak(Tree.Break breakStmt) //Break——Tree--by me
	{
		//helped
		tr.genBranch(loopExits.peek());
	}

	@Override
	public void visitCallExpr(Tree.CallExpr callExpr) //by me
	{
		Temp temp;
		if (callExpr.receiver != null)	
		{
			callExpr.receiver.accept(this);
			temp = callExpr.receiver.val;
			//helped
			if (callExpr.isArrayLength)
			{
				callExpr.val = tr.genLoad(callExpr.receiver.val, -OffsetCounter.WORD_SIZE);
				return;
			}
		}
		else
			temp = currentThis;
			
		//helped
		for (Tree.Expr e : callExpr.actuals) 
			e.accept(this);		
			
		if(!callExpr.symbol.isStatik())
			tr.genParm(temp);
			
		for (Tree.Expr e : callExpr.actuals) 
			tr.genParm(e.val);
		
		//I made a stupid mistake here;
		if (callExpr.symbol.isStatik()) 
			callExpr.val = tr.genDirectCall(callExpr.symbol.getFuncty().label, callExpr.symbol.getReturnType());
		else
		{
			Temp vTable = tr.genLoad(temp, 0);
			Temp func = tr.genLoad(vTable, callExpr.symbol.getOffset());
			callExpr.val = tr.genIndirectCall(func, callExpr.symbol.getReturnType());			
		}
	}

	@Override
	public void visitForLoop(Tree.ForLoop forLoop) //ForLoop——Tree-- by me
	{
		if (forLoop.init != null) 
			forLoop.init.accept(this);
			
		Label loop = Label.createLabel();
		tr.genMark(loop);
		
		if (forLoop.condition != null) 
			forLoop.condition.accept(this);
			
		Label exit = Label.createLabel();
		tr.genBeqz(forLoop.condition.val, exit);
		loopExits.push(exit);
		if (forLoop.loopBody != null) 
			forLoop.loopBody.accept(this);
		if (forLoop.update != null) 			
			forLoop.update.accept(this);
		tr.genBranch(loop);
		loopExits.pop();
		tr.genMark(exit);
	}

	@Override
	public void visitIf(Tree.If ifStmt) //If——Tree--by me
	{
		//此处不要push，否则break会出错，感谢老庄。
		ifStmt.condition.accept(this);		
		if (ifStmt.trueBranch != null) 
		{
			Label exit = Label.createLabel();			
			tr.genBeqz(ifStmt.condition.val, exit);
			ifStmt.trueBranch.accept(this);
			tr.genMark(exit);			
		}
		if (ifStmt.falseBranch != null) 
		{
			Label exit = Label.createLabel();			
			tr.genBnez(ifStmt.condition.val, exit);
			ifStmt.falseBranch.accept(this);
			tr.genMark(exit);	
		}
	}

	@Override
	public void visitNewArray(Tree.NewArray newArray) //NewArray——Expr.Temp--by me
	{
		newArray.elementType.accept(this);
		newArray.length.accept(this);
		newArray.val = tr.genNewArray(newArray.length.val);
	}

	@Override
	public void visitNewClass(Tree.NewClass newClass) //NewClass——Expr.Temp--by me
	{
		//helped
		//newClass.val = tr.genDirectCall(newClass.symbol.getNewFuncLabel(), newClass.symbol.getType());
		Label label = Label.createLabel("_" + newClass.className + "_New", true);
		newClass.val = tr.genDirectCall(label, newClass.symbol.getType());
	}

	@Override
	public void visitWhileLoop(Tree.WhileLoop whileLoop) //WhileLoop——Tree
	{
		Label loop = Label.createLabel();
		tr.genMark(loop);
		whileLoop.condition.accept(this);
		Label exit = Label.createLabel();
		tr.genBeqz(whileLoop.condition.val, exit);
		loopExits.push(exit);
		if (whileLoop.loopBody != null) 
		{
			whileLoop.loopBody.accept(this);
		}
		tr.genBranch(loop);
		loopExits.pop();
		tr.genMark(exit);
	}

	@Override
	public void visitTypeTest(Tree.TypeTest typeTest) //TypeTest——Expr.Temp
	{
		typeTest.instance.accept(this);
		typeTest.val = tr.genInstanceof(typeTest.instance.val,typeTest.symbol);
	}

	@Override
	public void visitTypeCast(Tree.TypeCast typeCast) //TypeCast——Expr.Temp--by me
	{
		//helped
		typeCast.expr.accept(this);
		tr.genClassCast(typeCast.expr.val, typeCast.symbol);
		typeCast.val = typeCast.expr.val;
	}
}
